package net.butfly.bus.context;

import java.io.Serializable;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import net.butfly.bus.Request;

public final class FlowNo implements Serializable, Cloneable {
	private static final long serialVersionUID = -3616807811490455640L;
	private static final DateFormat FLOW_NO_FORMAT = new SimpleDateFormat("ddHHmmssSSS");

	private Date time;
	private String random;
	private long serial;
	private String tx;
	private String ver;

	private FlowNo() {}

	public FlowNo(Request request) {
		super();
		FlowNo existed = (FlowNo) request.context(Context.Key.FlowNo.name());
		if (null == existed) {
			this.tx = request.code();
			this.ver = request.version();
			this.time = new Date();
			this.serial = 1;
			this.random = random();
			Context.setFlowNo(this);
		} else {
			this.copy(existed);
		}
	}

	public static FlowNo parse(String flowno) {
		if (null == flowno)
			return null;
		String[] fields = flowno.split("[.@#]");
		if (fields.length != 4)
			return null;
		FlowNo fn = new FlowNo();
		try {
			synchronized (FLOW_NO_FORMAT) {
				fn.time = FLOW_NO_FORMAT.parse(fields[0].substring(0, 11));
			}
			fn.random = fields[0].substring(11, 7);
			fn.tx = fields[2];
			fn.ver = fields[3];
			fn.serial = Long.parseLong(fields[1]);
			return fn;
		} catch (Exception e) {
			return null;
		}
	}

	@Override
	public FlowNo clone() {
		FlowNo that = new FlowNo();
		that.random = this.random;
		that.serial = this.serial;
		that.time = this.time;
		that.tx = this.tx;
		that.ver = this.ver;
		return that;
	}

	public void copy(FlowNo existed) {
		this.random = existed.random;
		this.serial = existed.serial;
		this.time = existed.time;
		this.tx = existed.tx;
		this.ver = existed.ver;
	}

	public FlowNo next() {
		FlowNo n = this.clone();
		n.serial++;
		return n;
	}

	@Override
	public String toString() {
		String timestamp;
		synchronized (FLOW_NO_FORMAT) {
			timestamp = FLOW_NO_FORMAT.format(time);
		}
		String s = Long.toString(serial);
		while (s.length() < 2)
			s = "0" + s;
		return new StringBuilder(timestamp).append(random).append(".").append(serial).append("@").append(tx).append("#")
				.append(ver).toString();
	}

	private static String RANDOM_SEED = "0123456789";
	private static int RANDOM_LENGTH = 7;

	private static synchronized String random() {
		StringBuilder rst = new StringBuilder();
		for (int i = 0; i < RANDOM_LENGTH; i++)
			rst.append(RANDOM_SEED.charAt((int) (Math.random() * RANDOM_SEED.length())));
		return rst.toString();
	}
}
